class TaskPro {
  _tasks = []
  _isRunning = false
  _currentIndex = 0

  addTask(task) {
    this._tasks.push(task)
  }

  async run() {
    if (this._isRunning) {
      return
    }
    this._isRunning = true
    await this._run()
  }

  async _run() {
    if (this._currentIndex >= this._tasks.length) {
      this._currentIndex = -1
      this._isRunning = false
      return
    }
    const task = this._tasks[this._currentIndex]
    const i = this._currentIndex
    await task(this._next.bind(this))
    const j = this._currentIndex
    if (i === j) {
      await this._next()
    }
  }

  async _next() {
    this._currentIndex++
    await this._run()
  }
}

const t = new TaskPro()

t.addTask(async (next) => {
  console.log(1, 'start')
  await next()
  console.log(1, 'end')
})

t.addTask(async (next) => {
  console.log(2)
  await next()
})

t.addTask(async (next) => {
  console.log(3)
  await next()
})

t.run() // 1 start, 2, 3, 1 end
